<?php
/*------------------------------------------------------------------------------
Copyright (C) 2006 J.Ducastel <http://jducastel.fr>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
------------------------------------------------------------------------------*/

/**
* accessing multidimensional data (object vars or array) through path syntax
* default syntax is "dot" i.e. some.path.to.data = $data['some']['path']['to']['data']
* @static
* @author J.Ducastel <http://jducastel.fr>
* @version 2006-10-25 15:54:05
*/
class dataPath {
/*------------------------------------------------------------------------------
                                                                     constructor
------------------------------------------------------------------------------*/
	/**
	*
	* @access
	* /
	function () {
		$this->__construct();
	}/**/
	
	/**
	* real constructor
	* @access
	* /
	function __construct() {
		
	}/**/
/*------------------------------------------------------------------------------
                                                                  public methods
------------------------------------------------------------------------------*/
	/**
	* set or get path segments separator character
	* @param string $new optional, single character, new separator
	* @access public
	* @return string
	*/
	function separator($new=null) {
		static $separator='.';
		if (is_string($new) and strlen($new)==1)
			$separator=$new;
		return $separator;
	}/**/
	
	/**
	* get targeted path from data
	* @static
	* @param mixed &$data array or object
	* @param string $path
	* @access public
	* @return mixed targeted path, by reference
	*/
	function & get(&$data, $path=null) {
		if (is_null($path)
		or (is_string($path) and strlen($path)==0))
			// path is blank, returning root
			return $data;
		// parsing path
		$childkey=datapath::cutpath($path);
		// reaching target
		// getting branch
		$child=&datapath::getchild($data,$childkey);
		if (!$child)
			// branch doesn't exist
			return $child;
		if (strlen($path)==0)
			// branch exist, end of path
			return $child;
		// branch exist,path continue
		$target=&datapath::get($child,$path);
		//echo '<p>datapath::get '.$path.' was '.wakapi::vardump($target).'</p>';
		return $target;
	}/**/
	
	/**
	* sets targeted path value
	* overwrite existing members if needed
	* creates members if needed
	* @param array|object &$data
	* @param mixed $value
	* @param string $path
	* @access public
	* @return bool
	*/
	function set(&$data,$value,$path=null) {
		//echo '<p>setting '.$path.' as '.wakapi::vardump($value).'</p>';
		// parsing path
		if (!$branch=datapath::cutpath($path)) {
			// setting root
			$data=$value;
			return true;
		}
		// is target reached ?
		if (strlen($path)==0) {
			return datapath::setchild($data,$branch,$value);
		}
		// continue along the path
		$child=&datapath::getchild($data,$branch);
		//if (!is_array($child) or !is_object($child))
		if (!$child)
			$child=&datapath::makechild($data,$branch);
		return datapath::set($child,$value,$path);
	}/**/
	
	/**
	* sets a key by reference 
	* @param array &$data source data
	* @param mixed &$ref reference
	* @param string $path
	* @access public
	* @return bool
	*/
	function setRef(&$data,&$ref,$path=null) {
		// is root ?
		if (is_null($path)
		or is_string($path) and strlen($path)==0) {
			$data=&$ref;
			return true;
		}
		// parsing path
		if (!$branch=datapath::cutpath($path)) {
			return false;
		}
		// is target reached ?
		if (strlen($path)==0) {
			return datapath::setchild($data,$branch,$ref);
		}
		// continue along the path
		if (!$child=&datapath::getchild($data,$branch))
			$child=&datapath::makechild($data,$branch);
		return datapath::setref($child,$path,$ref);
	}/**/
	
	/**
	* return child keys for a path if available
	* @param array &$data
	* @param string $path
	* @access public
	* @return array|false
	*/
	function getChildKeys(&$data,$path) {
		if (!$target=&datapath::get($data,$path))
			return false;
		switch (true) {
			case is_array($target):
				return array_keys($target);
			case is_object($target):
				return get_object_vars($target);
			default:
				return false;
		}
	}/**/
	
	/**
	* loops through path
	* @param array|object &$data
	* @param string $path
	* @access public
	* @return mixed
	* /
	function loop(&$data, $path) {
		static $looping=false;
		static $keys=array();
		static $ifni
	}/**/
	
	/**
	* 
	* @param
	* @access public
	* @return 
	* /
	function () {
		
	}/**/
	
	/**
	* 
	* @param
	* @access public
	* @return 
	* /
	function () {
		
	}/**/
/*------------------------------------------------------------------------------
                                                                      properties
------------------------------------------------------------------------------*/
	/**
	* @var array mapped data source
	*/
	var $data;
	var $separator='.';
/*------------------------------------------------------------------------------
                                                                 private methods
------------------------------------------------------------------------------*/
	/**
	* remove next path segment from passed path, and returns it
	* @param string &$path
	* @access protected
	* @return string next segment
	*/
	function cutPath(&$path) {
		if (!is_string($path))
			return false;
		$oldpath=$path;
		$sep=datapath::separator();
		// parsing path, separating current step from next one(s)
		$steps=explode($sep,$path); //var_dump($steps) ;
		$segment=array_shift($steps);
		$path=join($sep,$steps);
		// echo '<p>cutted '.$oldpath.' to '.$path.'</p>';
		return $segment;
	}/**/
	
	/**
	* return data member if exists
	* @param array|object &$data source data
	* @param string $key
	* @access protected
	* @return mixed
	*/
	function & getChild(&$data,$key) {
		switch (true) {
			// array, getting member
			case is_array($data) and isset($data[$key]):
				$child=&$data[$key];
				break;
			// object, getting member directly
			case is_object($data) and isset($data->$key):
				// echo "$key from object";
				$child=&$data->$key;
				break;
			// object, getting method result
			case is_object($data) and method_exists($data,$key):
				$child=$data->$key();
				break;
			default:
				$child=null;
		}
		return $child;
	}/**/
	
	/**
	* sets data member
	* @param array|object &$data source data
	* @param string $key
	* @param mixed $value
	* @access protected
	* @return bool
	*/
	function setChild(&$data,$key, &$value) {
		switch (true) {
			// array, setting member
			case is_array($data):
				$data[$key]=$value;
				return true;
				break;
			// object, accessing member
			case is_object($data) and isset($data->$key):
				$data->$key=$value;
				return true;
			default:
				//trigger_error("can't set child $key as data is scalar");
				// making it array
				$data=array();
				$data[$key]=&$value;
				return false;
		}
		// return $child;
	}/**/
	
	/**
	* creates data member and return its reference
	* @param array|object &$data source data
	* @param string $key
	* @param mixed $value
	* @access protected
	* @return bool
	*/
	function & makeChild(&$data,$key) {
		//echo "before :";
		//var_dump($data);
		switch (true) {
			// array, setting member
			case is_array($data):
				// target is an array
				$data[$key]=true;
				$child=&$data[$key];
				break;
			// object, setting member
			case is_object($data) and isset($data->$key):
				$data->$key=true;
				$child=&$data->$key;
				break;
			// object, method
			/*case is_object($data) and method_exists($data,$key):
				echo "<p>method $key from object</p>";
				$child=$data->$key($value);
				//return true;
				break;*/
			default:
				// making it array
				//echo "data was scalar : ".wakdebug::vardump($data);
				$data=array();
				$data[$key]=true;
				$child=&$data[$key];
				break;
				//$child=array();
				//$data=array($key=>true);
				//$child=&$data[$key];
				//$newdata=array($key=>&$child);
				//$data=&$newdata;
				//$data[$key]=&$child;
		}
		//echo "after :";
		//var_dump($data);
		return $child;
	}/**/
	
	/**
	* 
	* @param
	* @access protected
	* @return 
	* /
	function getOrMakeChild() {
		
	}/**/
	
	/**
	* checks path validity
	* @param
	* @access protected
	* @return bool
	*/
	function checkPath($path) {
		return is_string($path);
	}/**/
	
	/**
	* 
	* @param
	* @access protected
	* @return 
	* /
	function () {
		
	}/**/
	
	/**
	* 
	* @param
	* @access protected
	* @return 
	* /
	function () {
		
	}/**/
/*------------------------------------------------------------------------------
                                                                 private methods
------------------------------------------------------------------------------*/

	
	/**
	* 
	* @param
	* @access private
	* @return 
	* /
	function () {
		
	}/**/
}
?>